Skip to content

Add support for MG-GPO02Z / Sparkelec SGPO2TZ#12586

Draft
KablammoNick wants to merge 4 commits into
Koenkk:masterfrom
KablammoNick:patch-4
Draft

Add support for MG-GPO02Z / Sparkelec SGPO2TZ#12586
KablammoNick wants to merge 4 commits into
Koenkk:masterfrom
KablammoNick:patch-4

Conversation

@KablammoNick

@KablammoNick KablammoNick commented Jun 29, 2026

Copy link
Copy Markdown

Add support for MakeGood MG-GPO02Z / Sparkelec SGPO2TZ

Original external converter was 100% vibe-coded on my end, with a bunch of additional support and testing from @bdnstn in the original thread: Koenkk/zigbee2mqtt#31952

This PR adds support for the MakeGood MG-GPO02Z dual GPO with energy monitoring and RGB backlights,
also sold in Australia under the Sparkelec SGPO2TZ brand.

The Sparkelec appears to be a slightly different/newer version (marketed as V2) but testing shows the internals are the same.


Added fingerprints

Added fingerprint:

_TZE200_lq0ffndf

White label added:

Sparkelec SGPO2TZ

Device investigation

The implementation was based on datapoint information obtained from the Tuya IoT Platform device
model definitions, cross-referenced with live device behaviour reported in the linked issue.

The device uses TS0601 with the _TZE200_lq0ffndf manufacturer name. All datapoints are
communicated on Zigbee endpoint 1. Both sockets are mapped to endpoint 1 (i.e. {l1: 1, l2: 1}),
with socket identity carried in the DP number rather than the Zigbee endpoint.

Confirmed datapoints

DP Tuya name Type Notes
1 switch_1 Boolean Socket 1 on/off
2 switch_2 Boolean Socket 2 on/off
7 countdown_1 Integer Socket 1 auto-off countdown (s, 0–43200)
8 countdown_2 Integer Socket 2 auto-off countdown (s, 0–43200)
16 switch_backlight Boolean Backlight LED on/off
20 add_ele Integer Cumulative energy (kWh, scale 3 → ÷1000)
21 cur_current Integer Current (mA, scale 0 → ÷1000 for A)
22 cur_power Integer Power (W, scale 1 → ÷10)
23 cur_voltage Integer Voltage (V, scale 1 → ÷10)
29 relay_status_1 Enum Socket 1 power-on behavior (power_off/power_on/last)
30 relay_status_2 Enum Socket 2 power-on behavior (power_off/power_on/last)
101 child_lock Boolean Prevent physical button operation
107 backlight_color Raw Per-socket RGB color config — see notes below
136 all_on_off Boolean Master switch — both sockets simultaneously

Added functionality

DP 1 / 2 — switch_1 / switch_2

Mapped to state_l1 / state_l2 using tuya.valueConverter.onOff.

DP 7 / 8 — countdown_1 / countdown_2

Mapped to countdown_l1 / countdown_l2. The Tuya platform defines these as scale: 0,
so values are passed as raw integers (seconds). A custom converter normalises null and
false to 0 to avoid unexpected state values on first poll.

DP 16 — switch_backlight

Mapped to backlight_mode using tuya.exposes.backlightModeOffOn().

DP 20 — add_ele (energy)

Mapped to energy (kWh). The Tuya platform defines this as scale: 3, so the raw integer
is divided by 1000. The device reports incremental energy values rather than a running total;
a custom accumulating converter adds each reported delta to the previous state value and
ignores zero or negative jumps to prevent spurious resets.

DP 21 — cur_current

Mapped to current (A) using tuya.valueConverter.divideBy1000. Tuya reports in mA with
scale: 0.

DP 22 — cur_power

Mapped to power (W) using tuya.valueConverter.divideBy10. Tuya reports with scale: 1.

DP 23 — cur_voltage

Mapped to voltage (V) using tuya.valueConverter.divideBy10. Tuya reports with scale: 1.

DP 29 / 30 — relay_status_1 / relay_status_2

Mapped to power_on_behavior_l1 / power_on_behavior_l2 using a lookup converter with
Tuya enum values 0 = power_off, 1 = power_on, 2 = last.

DP 101 — child_lock

Mapped to child_lock using a boolean lookup (LOCK / UNLOCK).

DP 107 — backlight_color

The Tuya platform exposes a 21-byte raw payload encoding per-socket LED color for both on and
off states. The structure is:

byte  0:      mode (0 = single, 1 = multi)
bytes  1–10:  socket 1 (onBrightness, onHue×2, onSaturation×2, offBrightness, offHue×2, offSaturation×2)
bytes 11–20:  socket 2 (same layout)

Hue is 0–360, saturation is 0–1000, brightness is 0–100. A custom toZigbee converter
(backlightColorToZigbee) serialises a JSON object into this buffer and sends it via
tuya.sendDataPointRaw. The converter is only meaningful when backlight_mode is ON;
this is noted in the expose description.

Note: DP 107 is only broadcast by the device at power-on. There is no confirmed
read-back mechanism via z2m. The state is written optimistically and held in z2m state.
This behaviour was identified during investigation in the linked issue, where the initial
external converter omitted DP 107 entirely due to the difficulty of mapping it as a
standard Tuya enum.

DP 136 — all_on_off

Mapped to all_on_off using tuya.valueConverter.onOff. Listed in multiEndpointSkip
so it is not duplicated per endpoint.


Notes

  • Energy monitoring (DPs 20–23) is shared across both sockets; there is no per-socket
    breakdown. These are listed in multiEndpointSkip accordingly.
  • The device does not use separate Zigbee endpoints per socket — both sockets share
    endpoint 1. The endpoint function returns {l1: 1, l2: 1}.
  • No existing fingerprints or datapoint mappings were modified; this PR only adds the
    new MG-GPO02Z definition to makegood.ts.
  • The Sparkelec SGPO2TZ and MakeGood MG-GPO02Z are the same hardware sold under different
    brands in the Australian market.

Link to Sparkelec product page: https://agmelectrical.com.au/home-automation-wifi-intercoms-smart-switches-and-power-points/sparkelec-touch-zigbee-wifi-switches-and-power-points/double-power-point-10a-smart-zigbee-touch-glass-wall-switch-off-white-sparkelec-sgpo2tz-white.html

Link to picture pull request: Koenkk/zigbee2mqtt.io#5290

Add support for MG-GPO02Z / Sparkelec SGPO2TZ
KablammoNick and others added 3 commits June 30, 2026 10:44
Two changes made after automatic checks failed:

Line 7 — removed Fz from the type import (it was unused since backlightColorToZigbee uses Tz.Converter not Fz)
Line 90 — changed [...buf] to buf directly; sendDataPointRaw expects a Buffer, not a number[], so spreading it was the wrong approach
@KablammoNick KablammoNick marked this pull request as draft June 30, 2026 01:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant